import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/foundation.dart';
import 'package:yuan_hang_app/bean/user.dart';
import 'package:yuan_hang_app/http/app_response.dart';
import 'package:yuan_hang_app/res/w_constant.dart';
import 'package:yuan_hang_app/utils/common_req_utils.dart';
import 'package:yuan_hang_app/widget/w_toast.dart';

typedef OnDioRequest(RequestOptions options);
typedef OnDioResponse(Response response);
typedef OnDioError(DioException e);

class HttpUtils {
  late Dio _dio;
  BaseOptions? _options;
  static HttpUtils? _instance;
  static Interceptor? _interceptorsWrapper;
  static Interceptor? _logInterceptor;
  bool inProduction = const bool.fromEnvironment('dart.vm.product');

  factory HttpUtils(
          {bool needAuthor = true,
          bool needLog = true,
          OnDioRequest? onDioRequest,
          OnDioResponse? onDioResponse,
          OnDioError? onDioError}) =>
      _getInstance(
          needAuthor, needLog, onDioRequest, onDioResponse, onDioError);

  HttpUtils._internal(OnDioRequest? req, OnDioResponse? res, OnDioError? ode) {
    _options = BaseOptions(
        baseUrl: WConstant.API_HOST_OTTHER(),
        connectTimeout: const Duration(seconds: 30),
        receiveTimeout: const Duration(seconds: 30)); //不可加超时，否则下载文件会失败

    _dio = Dio(_options);
    if (!kIsWeb) {
      _dio.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
        final client = HttpClient();
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return true;
        };
        // client.findProxy = (uri)

        // };
        return client;
      }, validateCertificate: (certificate, host, port) {
        return true; //无条件允许https证书通行
      });
    }
    _logInterceptor = LogInterceptor(
        request: false,
        requestHeader: false,
        requestBody: false,
        responseHeader: false,
        responseBody: !inProduction);
    _interceptorsWrapper = InterceptorsWrapper(
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
      // if (options.headers["Authorization"] == null) {
      //   String? token = WConstant.userInfo.jwt;
      //   int userId = WConstant.userInfo.userId;
      //   if (token.isNotEmpty) {
      //     options.headers["Authorization"] = "Bearer $token";
      //   }
      //   options.headers["userId"] = userId;
      // }
      if (req != null) {
        req(options);
      }
      return handler.next(options);
    }, onResponse: (Response response, ResponseInterceptorHandler handler) {
      if (res != null) {
        res(response);
      }

      return handler.next(response);
    }, onError: (DioException error, ErrorInterceptorHandler handler) {
      if (ode != null) {
        ode(error);
      }
      return handler.next(error);
    });
  }

  static HttpUtils _getInstance(bool needAuthor, bool needLog,
      OnDioRequest? opt, OnDioResponse? res, OnDioError? ode) {

    _instance ??= HttpUtils._internal(opt, res, ode);
    _instance!._options!.baseUrl = WConstant.API_HOST_OTTHER();
    _instance!._dio.interceptors.clear();
    if (needLog) {
      _instance!._dio.interceptors.add(_logInterceptor!);
    }
    if (needAuthor) {
      _instance!._dio.interceptors.add(_interceptorsWrapper!);
    }

    return _instance!;
  }

  Future<AppResponse<T>> request<T>(String url, String method,
      {Object? data,
      Map<String, dynamic>? queryParams,
      CancelToken? cancelToken,
      Options? options,
      ProgressCallback? progressCallback,
      ProgressCallback? receiveCallback}) async {
    if (data != null && data is Map) {
      print(json.encode(data));
    }
    AppResponse<T> result;
    try {
      Options mOptions = Options();
      if (!(url.contains("loginAPP"))) {
        String? token = SpUtil.getString(WConstant.token) ?? "";
        if (token.isNotEmpty) {
          int userId = SpUtil.getInt(WConstant.userId) ?? 0;
          mOptions.headers = {
            "Authorization": token,
            "userId": userId,
            "userAgent": "app"
          };
          print(mOptions.headers);
        }
      }
      Response<String> response = await _dio.request(url,
          data: data,
          queryParameters: queryParams,
          cancelToken: cancelToken,
          options: mOptions..method = method,
          onSendProgress: progressCallback,
          onReceiveProgress: receiveCallback);
      Map<String, dynamic> map = json.decode(response.data.toString());
      result = AppResponse.fromJson(map);
      //     print(json.encode(result.data));
      if (result.resultCode == WConstant.invalidateToken) {
        ///401情况
        Toast.showAlertDialog(
            content: "该账号在其他设备登录",
            conformText: "退出登录",
            onConform: () {
              CommonReqUtils.loginOut();
            });
      }
    } on DioException catch (error) {
      LogUtil.e("request error-- $error");
      if (error.type == DioExceptionType.connectionTimeout ||
          error.type == DioExceptionType.receiveTimeout) {
        Toast.toast("网络请求超时,请稍后再试");
        result = AppResponse(WConstant.otherError, "网络请求超时,请稍后再试", null);
        return result;
      } else {
        result = AppResponse(WConstant.otherError, error.message, null);
        return result;
      }
      return result;
    }
    return result;
  }

  Future<AppResponse<T>> get<T>(String url,
      {Object? data,
      Map<String, dynamic>? queryParams,
      CancelToken? cancelToken,
      Options? options,
      ProgressCallback? progressCallback,
      ProgressCallback? receiveCallback}) async {
    return request(url, "GET",
        data: data,
        queryParams: queryParams,
        cancelToken: cancelToken,
        options: options,
        progressCallback: progressCallback,
        receiveCallback: receiveCallback);
  }

  Future<AppResponse<T>> post<T>(String url,
      {Object? data,
      Map<String, dynamic>? queryParams,
      CancelToken? cancelToken,
      Options? options,
      ProgressCallback? progressCallback,
      ProgressCallback? receiveCallback}) async {
    return request(url, "POST",
        data: data,
        queryParams: queryParams,
        cancelToken: cancelToken,
        options: options,
        progressCallback: progressCallback,
        receiveCallback: receiveCallback);
  }

  Future<AppResponse<T>> delete<T>(String url,
      {Object? data,
      Map<String, dynamic>? queryParams,
      CancelToken? cancelToken,
      Options? options,
      ProgressCallback? progressCallback,
      ProgressCallback? receiveCallback}) async {
    return request(url, "DELETE",
        data: data,
        queryParams: queryParams,
        cancelToken: cancelToken,
        options: options,
        progressCallback: progressCallback,
        receiveCallback: receiveCallback);
  }

  Future<AppResponse<T>> put<T>(String url,
      {Object? data,
      Map<String, dynamic>? queryParams,
      CancelToken? cancelToken,
      Options? options,
      ProgressCallback? progressCallback,
      ProgressCallback? receiveCallback}) async {
    return request(url, "PUT",
        data: data,
        queryParams: queryParams,
        cancelToken: cancelToken,
        options: options,
        progressCallback: progressCallback,
        receiveCallback: receiveCallback);
  }

  Future<Response> downloadFile(String urlPath, String savePath,
      {Options? option,
      Map<String, dynamic>? queryParameters,
      ProgressCallback? onReceiveProgress}) {
    return _dio
        .download(urlPath, savePath,
            options: option,
            queryParameters: queryParameters,
            onReceiveProgress: onReceiveProgress)
        .then((response) {
      return response;
    });
  }
}
